package org.alfresco.rfernandes.zkcmis.app;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.alfresco.cmis.client.AlfrescoAspects;
import org.alfresco.rfernandes.zkcmis.login.User;
import org.alfresco.rfernandes.zkcmis.login.UserCredentialManager;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.ObjectType;
import org.apache.chemistry.opencmis.client.api.Property;
import org.apache.chemistry.opencmis.client.api.QueryResult;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.metainfo.ComponentInfo;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Button;
import org.zkoss.zul.Label;
import org.zkoss.zul.Row;
import org.zkoss.zul.Rows;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Vbox;
import org.zkoss.zul.Window;

public class AppCtrl extends SelectorComposer<Window> {

	/**
	 * 
	 */
	private static final long serialVersionUID = -3630371988339078797L;
	@Wire
	private Label repoName;
	@Wire
	private Rows spaceObjects;
	@Wire
	private Button backBtn;
	@Wire
	private Textbox noteName;
	@Wire
	private Textbox noteContent;
	@Wire
	private Label noteType;
	@Wire
	private Rows noteProperties;
	@Wire
	private Rows aspectsValues;
	@Wire
	private Textbox queryLbl;
	@Wire
	private Rows searchResults;
	@Wire
	private Vbox queryItemProperties;

	private User user;
	private Folder folder;
	private Document documentEdited = null;

	private boolean loggedOut = false;

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Listen("onClick=#searchBtn")
	public void search() throws IOException {
		emptyChildren(searchResults);
		emptyChildren(queryItemProperties);
		ItemIterable<QueryResult> queryResult = user.getSession().query(
				queryLbl.getValue(), false);
		for (final QueryResult item : queryResult) {
			Row row = new Row();
			PropertyData data= item.getPropertyById(
					"cmis:name");
			if(data==null)
				continue;
			Label label = new Label( data!=null?getStringValue( data.getValues()):"null" );
			row.appendChild(label);
			row.setStyle("cursor: pointer;");
			row.addEventListener("onClick", new EventListener() {

				public void onEvent(Event event) throws Exception {
					fillItemProperties(item);
				}

			});
			searchResults.appendChild(row);
		}
	}

	private void fillItemProperties(QueryResult item) {
		emptyChildren(queryItemProperties);
		for (@SuppressWarnings("rawtypes")
		PropertyData prop : item.getProperties()) {
			queryItemProperties.appendChild(new Label(prop.getQueryName()
					+ ": " + getStringValue(prop.getValues())));
		}
	}

	@Listen("onClick=#logoutBtn")
	public void logout() throws IOException {
		loggedOut = true;
		UserCredentialManager.setUser(null);
		Executions.sendRedirect("..");
	}

	@Listen("onClick=#backBtn")
	public void back() throws IOException {
		Folder parent = folder.getFolderParent();
		if (parent != null) {
			fillChildren(parent);
		}
	}

	@Listen("onClick=#updateFolderBtn")
	public void updateFolder() throws IOException {
		String name = noteName.getValue();
		if (name.trim().equals("")) {
			return;
		}
		Map<String, String> property = new HashMap<String, String>();
		// note we are adding an aspect
		property.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
		property.put(PropertyIds.NAME, name);
		folder.updateProperties(property);
	}

	@Listen("onClick=#saveDocBtn")
	public void saveDocument() throws IOException {
		String name = noteName.getValue();
		if (name.trim().equals("")) {
			return;
		}
		byte[] buffer = noteContent.getValue().getBytes("UTF-8");
		ContentStream stream = user
				.getSession()
				.getObjectFactory()
				.createContentStream(name, buffer.length,
						"text/plain; charset=UTF-8",
						new ByteArrayInputStream(buffer));
		Map<String, String> property = new HashMap<String, String>();
		// note we are adding an aspect
		property.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document,P:cm:titled");
		property.put(PropertyIds.NAME, name);
		if (documentEdited != null) {
			documentEdited.updateProperties(property, true);
			documentEdited.setContentStream(stream, true);
		} else {
			folder.createDocument(property, stream, VersioningState.MAJOR);
		}
		noteContent.setValue("");
		noteName.setValue("");
		fillChildren(folder);
	}

	@Listen("onClick=#saveFolderBtn")
	public void saveFolder() throws IOException {
		String name = noteName.getValue();
		if (name.trim().equals("")) {
			return;
		}
		Map<String, String> property = new HashMap<String, String>();
		// note we are adding an aspect
		property.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
		property.put(PropertyIds.NAME, name);
		folder.createFolder(property);
		noteName.setValue("");
		fillChildren(folder);
	}

	@Override
	public ComponentInfo doBeforeCompose(Page page, Component parent,
			ComponentInfo compInfo) {
		user = UserCredentialManager.getUser();
		if (user == null) {
			try {
				logout();
			} catch (IOException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		return compInfo;
	}

	@Override
	public void doAfterCompose(Window comp) throws Exception {
		super.doAfterCompose(comp);
		if (loggedOut)
			return;
		fillChildren(user.getSession().getRootFolder());
		repoName.setValue(user.getRepository().getName() + " - "
				+ folder.getName());
	}

	private void refresh() {
		fillChildren(folder);
	}

	private void fillChildren(Folder space) {
		folder = space;
		noteName.setValue(folder.getName());
		noteContent.setValue("");
		documentEdited = null;
		noteType.setValue(folder.getType().getId() + " - base type: "
				+ folder.getBaseType().getId());
		fillProperties(folder);
		fillAspects(folder);
		if (folder.getFolderParent() == null) {
			backBtn.setDisabled(true);
		} else {
			backBtn.setDisabled(false);
		}
		emptyChildren();
		ItemIterable<CmisObject> children = space.getChildren();
		for (CmisObject o : children) {
			spaceObjects.appendChild(getRow(o));
		}
	}

	private void emptyChildren() {
		emptyChildren(spaceObjects);
	}

	private static void emptyChildren(Component comp) {
		List<Component> comps = comp.getChildren();
		for (int i = comps.size(); i > 0; i--) {
			comps.get(i - 1).setParent(null);
		}
	}

	private void fillProperties(CmisObject cmisObject) {
		emptyChildren(noteProperties);
		for (@SuppressWarnings("rawtypes")
		Property prop : cmisObject.getProperties()) {
			Row row = new Row();
			row.appendChild(new Label(prop.getDisplayName()));
			row.appendChild(new Label(getStringValue(prop.getValue())));
			noteProperties.appendChild(row);
		}
	}

	private void fillAspects(CmisObject cmisObject) {
		emptyChildren(aspectsValues);
		for (ObjectType prop : ((AlfrescoAspects)cmisObject).getAspects()) {
			Row row = new Row();
			row.appendChild(new Label(prop.getDisplayName()));
			aspectsValues.appendChild(row);
		}
	}

	@SuppressWarnings({ "rawtypes", "deprecation" })
	private String getStringValue(Object value) {
		if (value instanceof List) {
			String s = "";
			for (Object o : ((List) value)) {
				s += getStringValue(o) + ";";
			}
			return s.length() != 0 ? s.substring(0, s.length() - 1) : s;
		} else if (value instanceof GregorianCalendar) {
			return ((GregorianCalendar) value).getTime().toLocaleString();
		} else {
			return value != null ? value.toString() : null;
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Row getRow(final CmisObject cmisObject) {
		Row row = new Row();
		Label label = new Label(cmisObject.getName());
		if (cmisObject instanceof Folder) {
			row.addEventListener("onClick", new EventListener() {

				public void onEvent(Event event) throws Exception {
					fillChildren((Folder) cmisObject);
				}

			});
			label.setStyle("font-weight: bold;");
		} else {
			row.addEventListener("onClick", new EventListener() {

				public void onEvent(Event event) throws Exception {
					documentEdited = (Document) cmisObject;
					noteType.setValue(cmisObject.getType().getId()
							+ " - base type: "
							+ cmisObject.getBaseType().getId());
					fillProperties(cmisObject);
					fillAspects(cmisObject);
					noteName.setValue(cmisObject.getName());
					try {
						noteContent
								.setValue(getContentAsString(((Document) cmisObject)
										.getContentStream()));
					} catch (Exception e) {

					}
				}

			});
		}
		row.appendChild(label);
		Button deleteButton = new Button("delete");
		deleteButton.addEventListener("onClick", new EventListener() {

			public void onEvent(Event arg0) throws Exception {
				if (cmisObject instanceof Document) {
					cmisObject.delete(true);
				} else {
					((Folder) cmisObject).deleteTree(true, null, true);
				}
				refresh();
			}
		});
		row.appendChild(deleteButton);
		row.setStyle("cursor: pointer;");
		return row;
	}

	private static String getContentAsString(ContentStream stream)
			throws IOException {
		InputStream in2 = stream.getStream();
		StringBuffer sbuf = null;
		sbuf = new StringBuffer(in2.available());
		int count;
		byte[] buf2 = new byte[100];
		while ((count = in2.read(buf2)) != -1) {
			for (int i = 0; i < count; i++) {
				sbuf.append((char) buf2[i]);
			}
		}
		in2.close();
		return sbuf.toString();
	}
}
